Skip to content

fix(spec): 给 packages/spec 的 vitest 设 testTimeout 60s —— 止血,不再把无关 PR 踢出合并队列 (#4850) - #4856

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-4850-spec-vitest-timeout
Aug 3, 2026
Merged

fix(spec): 给 packages/spec 的 vitest 设 testTimeout 60s —— 止血,不再把无关 PR 踢出合并队列 (#4850)#4856
os-zhuang merged 1 commit into
mainfrom
claude/issue-4850-spec-vitest-timeout

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #4850

Related: #4796(保持打开 —— 方向 2「把导出面解析提成构建期产物」仍归 spec 车道,本单只是它的止血)

改了什么

packages/spec/vitest.config.tstest 块加一行 testTimeout: 60_000(带注释),外加一个 patch changeset。改动面仅此两个文件,不碰任何 .zod.ts、测试逻辑、spec-changes.json、liveness 台账与生成基线。

根因复核(我自己重新取证了一遍,不是照抄 issue)

三条都在 origin/main 上确认过:

  1. 确实没有 testTimeout —— git show origin/main:packages/spec/vitest.config.tstest 块只有 globals / environment / include / coverage,吃 vitest 默认 5000ms。
  2. 确实是 12 个文件在用例内 (await import('typescript')).defaultts.createProgram + getTypeChecker。我遍历 origin/main 上全部 282 个 spec 测试文件逐个 grep,命中数正好 12,与 issue 列的清单完全一致。
    ⚠️ 顺带一个坑:共享检出当时落后 origin/main 63 个提交,直接在工作树里 grep 只会命中 0 个(那几段是后来的提交加的)。这类核查必须对 origin/main 做。
  3. 没有别处在给这个包设超时 —— 仓库根没有 vitest.config.* 也没有 vitest.workspace.ts;packages/spec 的 test 脚本是裸 vitest run(无 --testTimeout);turbo.jsontest task 不传参;workflows 里没有任何 VITEST_* / TEST_TIMEOUT 覆盖。全仓已有 testTimeout 的 5 个包(metadata-fsqa/http-conformanceclientdriver-mongodbplugin-auth)都是各自的包内配置,管不到 spec。所以这一行是真正生效的那一层,不是被更高优先级配置盖掉的空操作。

验证「它真的生效了」

没有只靠读配置。临时加了一条 await new Promise(r => setTimeout(r, 6000)) 的探针用例:

  • 改之前:Error: Test timed out in 5000ms. —— vitest 自己在报错里提示 configure it globally with "testTimeout"
  • 改之后:Test Files 1 passed (1),Duration 6.36s

探针用例已删除,不在提交里(git status 干净,diff 只有上述两个文件)。

这 12 个用例的实际耗时(issue 要求先测量再定数)

空载 runner 上跑这 12 个文件:12 passed / 497 tests,用 JSON reporter 取单用例耗时:

指标 实测
最慢单用例 3425ms(system/environment-artifact 的导出面解析)
超过 3000ms 的用例 5 条
超过 5000ms 的用例 0 条(空载时)
60s 相对最慢用例的余量 约 17.5 倍

结论:60s 余量充足,不薄。 同时这组数字正好解释了 issue 里那个「PR 分支一次没红过、5 次全发生在队列里」的现象 —— 空载最慢 3.4s 顶 5000ms 的预算,余量只有约 1.46 倍,队列 runner 同时构建多个 PR 批次时轻易被吃掉;而 60s 要被吃掉需要 17 倍的减速,属于真挂起才会触发,不会把真 bug 盖住。

但请注意方向 2 的紧迫性没有被这一行降低: 这 12 个文件里,耗时超过 800ms 的 16 条用例合计 39.0s,占这 12 个文件全部用例耗时(44.2s)的 88% —— 每次 CI 跑一遍就是近 40 秒纯 TypeScript 编译,而且随导出面增长。止血归止血,#4796 的本体仍然值得做。

为什么设在配置层而不是逐条加

PR #4506 就是逐条 { timeout: … },只覆盖了当时红的那几条;池子有 12 个,而且新增的照样漏。配置层一次覆盖全部,第 13 个到达即被覆盖。60s 沿用 #4506 当初给同族用例定的值,不引入新数字。

测试

均在 flock -w 7200 /tmp/os-heavy-verify.lock 下、带 NODE_OPTIONS=--max-old-space-size=4096--maxWorkers=2 执行:

  • pnpm --filter @objectstack/spec testTest Files 294 passed (294) / Tests 7362 passed (7362),Duration 102.26s
  • pnpm --filter @objectstack/spec typechecktsc --noEmit 无输出(通过)

未触及任何生成物对应的输入面(.describe()、公共导出、authorable key、ADR-0087 registry、SKILL.md、react-blocks),故未重新生成 spec 的生成物。

跨车道说明

packages/spec/** 常态归另一个 PM 车道,本单是经维护者批准的跨车道止血,因此改动面严格限死在 vitest.config.ts 一行 + changeset。过程中在别处看到的问题一律只写进报告,未动手。


🤖 Generated with Claude Code

https://claude.ai/code/session_015Br2xsJsczFsTR9bvbh2Ny


Generated by Claude Code

packages/spec/vitest.config.ts never set testTimeout, so every case ran under
vitest's 5000ms default. Twelve tests load the TypeScript compiler in-case and
type-resolve the whole export surface (ts.createProgram + getTypeChecker),
which is seconds of work by construction.

Measured on an idle runner the slowest such case is 3.4s against a 5000ms
budget — green on a PR branch, too thin on a merge-queue runner building
several PRs at once. Five failures in one night, all inside the queue, each
evicting an unrelated PR.

Set at the config layer so all twelve are covered, and so a thirteenth is
covered on arrival — PR #4506 set the same 60s value case-by-case and only
covered the ones red at the time.

Stop-the-bleeding only; the underlying per-run TypeScript compilation cost
stays tracked in #4796.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Br2xsJsczFsTR9bvbh2Ny
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 3, 2026 10:19am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/s labels Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Copy link
Copy Markdown
Contributor Author

复核通过 —— ACCEPT,已标 ready 并送合并队列

派发时我给这一单加了三条 issue 正文没有的要求,因为「一个修没验证过的配置的 PR,如果自己也没验证,就是在犯同一种错」。三条逐条兑现了:

1. 核实这一行不会被别处遮蔽。 这是我最担心的失败模式 —— 加一行看似修好、实则被更高优先级配置吃掉。dev 逐个排除了:无根级 vitest.config.*、无 vitest.workspace.ts、test 脚本是裸 vitest run、turbo 无透传、workflow 里无 VITEST_* / TEST_TIMEOUT这一行确实生效。

2. 证明新值真的作用到了。 临时 6000ms 探针用例,改前 Error: Test timed out in 5000ms.(vitest 自己还提示 configure it globally with "testTimeout"),改后 Duration 6.36s 通过,随后删除探针 —— 最终 git status 只剩 M vitest.config.ts + ?? .changeset/...。这是可证伪的验证,不是"我改了所以应该好了"。

3. 量了真实耗时,而且这个数改变了结论。

最慢用例 3425ms(system/environment-artifact 导出面解析)
超 3000ms 的用例 5 条
空闲 runner 上超 5000ms 的 0 条

所以 5000ms 是「紧,但不是刚好」—— 余量只有 1.46 倍,而拥挤的队列 runner 正好把它吃掉。60s 是最慢用例的 ~17.5 倍,宽到能吸收争用,又不至于把真正的死循环掩盖成"跑得久"。这个数据本身就是 #4796 命中率的解释

范围与边界都守住了

2 个文件 / 44 行,.zod.ts、测试逻辑、spec-changes.json、liveness 台账、生成基线一个没碰。全量回归 294 passed / 7362 passed,typecheck 干净。

#4796 保持打开这一点它做了主动核验 —— 不是"我没写 Fixes 所以应该没事",而是把存进 GitHub 的 PR 正文读回来确认 4796 附近没有任何 closing 动词、且正文没被 sanitizer 破坏。这正是本仓 <x> 吞正文那个坑该有的防法。

它顺手报的一个数,把 #4796 的紧迫性钉死了

dev 在 #4796 上留了测量数据:这 12 个文件 88% 的测试时间(44.2s 中的 39.0s)是纯 TypeScript 编译,每轮 CI 重复,且随导出面增长。并且明确指出:

#4850 的 timeout 调整节省的编译时间为零 —— 它只是把突破阈值从 1.46 倍挪到 17.5 倍。

说得对。这是止血,不是解。方向 2(把导出面解析提成构建期产物)能同时回收那 39 秒并且把一个非确定性超时变成确定性比对。#4796 仍归 spec 车道,建议按"仍然紧急"对待。


⚠️ 给后续 agent 的环境提醒(dev 报的,值得所有人知道):共享检出 /home/user/objectstack 落后 origin/main 63 个提交,在它的工作树里 grep 那 12 个文件会得到零命中。任何针对 main 的事实核验必须走 git grep origin/main: 或先 fetch,否则会得到假阴性。我自己今早第一次 grep 也踩了这个,靠改查 origin/main 才拿到真实结果。


Generated by Claude Code

@os-zhuang
os-zhuang added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit d40f43a Aug 3, 2026
22 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-4850-spec-vitest-timeout branch August 3, 2026 10:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

止血:packages/spec 的 vitest 缺 testTimeout —— 12 个跑 TS 编译器的用例配在 5s 默认值下

2 participants